/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hisense.settings;
import com.google.android.tv.settings.TvSettings;
import com.android.settings.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.widget.Toast;
import com.hisense.settings.EditPinPreference;
/**
 * Implements the preference screen to enable/disable app lock and
 * also the dialogs to change the app lock PIN.
 * <p>
 * Enabling the app lock will prompt the user to enter a new PIN twice.
 * Disabling the app lock will prompt the user for the current PIN.
 * <p>
 * The PIN can only be changed when the app lock is enabled.  In that case,
 * the the user will first be prompted for the old pin, and then will be
 * prompted to enter a new PIN twice.
 *
 * Forked from com.android.settings.IccLockSettings.
 * TODO: Merge back together with IccLockSettings if possible.
 */
public class AppLockSettings extends PreferenceActivity
        implements EditPinPreference.OnPinEnteredListener {

    private static final int DIALOG_CONFIRM_NEW_PIN = 2;
    private static final int DIALOG_CONFIRM_CHANGED_PIN = 3;

    // Constants for saving/restoring instance state
    private static final String DIALOG_STATE = "dialog_state";
    private static final String ERROR = "error";
    private static final String NEW_PIN = "new_pin";

    enum DialogMode {
        // State when enabling lock and entering a new pin
        LOCK_ENTER_NEW,
        // State when disabling lock
        UNLOCK,
        // State when entering the old pin
        OLD,
        // State when entering the new pin
        ENTER_NEW
    }

    // Keys in xml file
    private static final String APP_LOCK_TOGGLE = "app_lock_toggle";
    private static final String APP_LOCK_PIN = "app_lock_pin";

    // Which dialog to show next when popped up
    private DialogMode mDialogState = null;
    private String mNewPin;
    private String mError;

    private int mMinPinLength;
    private EditPinPreference mPinDialog;
    private CheckBoxPreference mPinToggle;

    private Resources mRes;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.app_lock_settings);

        mPinDialog = (EditPinPreference) findPreference(APP_LOCK_PIN);
        mPinToggle = (CheckBoxPreference) findPreference(APP_LOCK_TOGGLE);

        mPinDialog.setOnPinEnteredListener(this);

        // Don't need any changes to be remembered
        getPreferenceScreen().setPersistent(false);

        mRes = getResources();

        mMinPinLength = mRes.getInteger(R.integer.min_pin_length);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mDialogState != null) {
            outState.putString(DIALOG_STATE, mDialogState.name());
        }
        outState.putString(ERROR, mError);
        outState.putString(NEW_PIN, mNewPin);
    }

    @Override
    protected void onRestoreInstanceState(Bundle state) {
        super.onRestoreInstanceState(state);
        String dialogState = state.getString(DIALOG_STATE);
        if (dialogState != null) {
            mDialogState = DialogMode.valueOf(dialogState);
        }
        mError = state.getString(ERROR);
        mNewPin = state.getString(NEW_PIN);
    }

    @Override
    protected void onResume() {
        super.onResume();

        boolean enabled =
                TvSettings.Secure.getBoolean(getContentResolver(),
                        TvSettings.Secure.APPLICATION_LOCK_ENABLED, false) &&
                Settings.Secure.getString(getContentResolver(),
                        TvSettings.Secure.APPLICATION_LOCK_PIN) != null;
        mPinToggle.setChecked(enabled);
        mPinDialog.setEnabled(enabled);

        if (mDialogState != null) {
            showPinDialog();
        } else {
            // Prep for standard click on "Change PIN"
            resetDialogState();
        }
    }

    private void showPinDialog() {
        if (mDialogState == null) {
            return;
        }
        setDialogValues();

        mPinDialog.showPinDialog();
    }

    private void setDialogValues() {
        mPinDialog.setText(null);
        String message = "";
        switch (mDialogState) {
            case LOCK_ENTER_NEW:
                mPinDialog.setDialogTitle(R.string.app_lock_enable_app_lock);
                message = mRes.getString(R.string.app_lock_enter_new);
                break;
            case UNLOCK:
                mPinDialog.setDialogTitle(R.string.app_lock_disable_app_lock);
                message = mRes.getString(R.string.app_lock_enter_pin);
                break;
            case OLD:
                mPinDialog.setDialogTitle(R.string.app_lock_change_pin);
                message = mRes.getString(R.string.app_lock_enter_old);
                break;
            case ENTER_NEW:
                mPinDialog.setDialogTitle(R.string.app_lock_change_pin);
                message = mRes.getString(R.string.app_lock_enter_new);
                break;
        }
        if (mError != null) {
            message = mError + "\n" + message;
            mError = null;
        }
        mPinDialog.setDialogMessage(message);
    }

    public void onPinEntered(EditPinPreference preference,
            boolean positiveResult) {
        if (!positiveResult) {
            resetDialogState();
            return;
        }

        String pin = preference.getText();
        switch (mDialogState) {
            case LOCK_ENTER_NEW:
                if (pin.length() < mMinPinLength) {
                    mError = mRes.getQuantityString(
                            R.plurals.app_lock_too_short, mMinPinLength,
                            mMinPinLength);
                    showPinDialog();
                } else {
                    mNewPin = pin;
                    mDialogState = null;
                    showDialog(DIALOG_CONFIRM_NEW_PIN);
                }
                break;
            case UNLOCK:
                if (!validatePin(pin)) {
                    mError = mRes.getString(R.string.app_lock_bad_pin);
                    mDialogState = DialogMode.UNLOCK;
                    showPinDialog();
                } else {
                    appLockChanged(false);
                }
                break;
            case OLD:
                if (validatePin(pin)) {
                    mDialogState = DialogMode.ENTER_NEW;
                    mError = null;
                } else {
                    mDialogState = DialogMode.OLD;
                    mError = mRes.getString(R.string.app_lock_bad_pin);
                }
                showPinDialog();
                break;
            case ENTER_NEW:
                if (pin.length() < mMinPinLength) {
                    mError = mRes.getQuantityString(
                            R.plurals.app_lock_too_short, mMinPinLength,
                            mMinPinLength);
                    showPinDialog();
                } else {
                    mNewPin = pin;
                    mDialogState = null;
                    showDialog(DIALOG_CONFIRM_CHANGED_PIN);
                }
                break;
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_CONFIRM_NEW_PIN:
                return new AlertDialog.Builder(AppLockSettings.this)
                    .setTitle(R.string.app_lock_confirm_new_pin_title)
                    .setMessage(mRes.getString(
                            R.string.app_lock_confirm_new_pin_message, mNewPin))
                    .setPositiveButton(android.R.string.ok,
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            appLockPinChanged(mNewPin);
                            appLockChanged(true);
                        }
                    })
                    .setNegativeButton(android.R.string.cancel,
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            resetDialogState();
                        }
                    })
                    .create();
            case DIALOG_CONFIRM_CHANGED_PIN:
                return new AlertDialog.Builder(AppLockSettings.this)
                    .setTitle(R.string.app_lock_confirm_changed_pin_title)
                    .setMessage(mRes.getString(
                            R.string.app_lock_confirm_changed_pin_message,
                            mNewPin))
                    .setPositiveButton(android.R.string.ok,
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            appLockPinChanged(mNewPin);
                        }
                    })
                    .setNegativeButton(android.R.string.cancel,
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            resetDialogState();
                        }
                    })
                    .create();
        }
        return null;
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        AlertDialog alertDialog;
        switch (id) {
            case DIALOG_CONFIRM_NEW_PIN:
                alertDialog = (AlertDialog) dialog;
                alertDialog.setMessage(mRes.getString(
                        R.string.app_lock_confirm_new_pin_message, mNewPin));
                alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
                        .requestFocus();
                break;
            case DIALOG_CONFIRM_CHANGED_PIN:
                alertDialog = (AlertDialog) dialog;
                alertDialog.setMessage(mRes.getString(
                        R.string.app_lock_confirm_changed_pin_message,
                        mNewPin));
                alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
                        .requestFocus();
                break;
        }
    }

    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
            Preference preference) {
        if (preference == mPinToggle) {
            // Get the new, preferred state
            boolean toState = mPinToggle.isChecked();
            // Flip it back and pop up pin dialog
            mPinToggle.setChecked(!toState);
            resetDialogState();
            mDialogState = toState ? DialogMode.LOCK_ENTER_NEW :
                                     DialogMode.UNLOCK;
            showPinDialog();
        } else if (preference == mPinDialog) {
            mDialogState = DialogMode.OLD;
            return false;
        }
        return true;
    }

    private void appLockChanged(boolean toState) {
        mPinToggle.setChecked(toState);
        mPinDialog.setEnabled(toState);
        TvSettings.Secure.putBoolean(getContentResolver(),
                TvSettings.Secure.APPLICATION_LOCK_ENABLED, toState);
        resetDialogState();
    }

    private void appLockPinChanged(String pin) {
        Settings.Secure.putString(getContentResolver(),
                TvSettings.Secure.APPLICATION_LOCK_PIN, pin);
        Toast.makeText(this, mRes.getString(R.string.app_lock_change_succeeded),
                Toast.LENGTH_SHORT)
                .show();
        resetDialogState();
    }

    private boolean validatePin(String pin) {
        String correctPin = Settings.Secure.getString(getContentResolver(),
                TvSettings.Secure.APPLICATION_LOCK_PIN);
        if (correctPin == null) {
            throw new IllegalStateException(
                    "Cannot validate pin when it has not been set.");
        }
        return correctPin.equals(pin);
    }

    private void resetDialogState() {
        mError = null;
        mDialogState = DialogMode.OLD; // Default for when Change PIN is clicked
        setDialogValues();
        mDialogState = null;
    }
}
